use std::ffi::OsString;
use std::mem;
//use std::ptr;

use std::env;

use std::fs;
use std::fs::DirBuilder;
use std::fs::File;
//use std::fs::*;
use std::path::Path;
use std::path::PathBuf;

use std::io::Read;
use std::io::Seek;
use std::io::Write;

use colored::*;

// import window api
use windows::{
    core::*,
    Win32::{
        Foundation::*,
        System::Threading::{
            CreateProcessW, GetExitCodeProcess, WaitForSingleObject, CREATE_NEW_CONSOLE,
            /*CREATE_NO_WINDOW,*/ PROCESS_CREATION_FLAGS, PROCESS_INFORMATION,
            CREATE_NO_WINDOW, STARTUPINFOW,
        },
        //UI::WindowsAndMessaging::*,
    },
};

use windows::Win32::System::Threading::OpenProcess;
use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
use windows::Win32::System::Threading::PROCESS_ALL_ACCESS;
use windows::Win32::System::Threading::INFINITE;
use windows::Win32::Foundation::WIN32_ERROR;

use crate::reader_config::my_exit_process;
use crate::Config;

fn my_exit_process_msg(msg: &str) {
    println!("{}", msg.red());
    println!("{}", "程序终止执行后续步骤...".red());
    std::process::exit(1);
}

#[derive(Default, Debug)]
pub struct Core {
    pub build_dir: Option<PathBuf>,
}

impl Core {
    pub fn is_exist_build() -> bool {
        let mut path = env::current_dir().unwrap();
        path.push("build");
        path.as_path().exists()
    }

    pub fn create_build(cfg:&Config) -> Option<PathBuf> {
        let mut path = PathBuf::new();
        path.push(cfg.project_root.clone());

        path.push("build");

        if !Self::is_exist_build() {
            let _res = DirBuilder::new().create(path.clone());
            return Some(path);
        } else {
            return Some(path);
        }
    } //end fn

    fn traverse_directory<'a>(path: &Path, nasm_vec: &'a mut Vec<OsString>) -> &'a Vec<OsString> {
        if let Ok(entries) = fs::read_dir(path) {
            for entry in entries {
                if let Ok(entry) = entry {
                    let path = entry.path();
                    if path.is_file() {
                        let extran_name = path.extension();
                        match extran_name {
                            Some(ext) => {
                                if ext == "asm" {
                                    nasm_vec.push(path.into_os_string());
                                }
                            }
                            None => {}
                        }
                    } else if path.is_dir() {
                        Self::traverse_directory(&path, nasm_vec);
                    }
                }
            } //enf for
        } //end if 1
        nasm_vec
    } //end fn
    pub fn get(cfg: &Config) -> Vec<OsString> {
        let mut vec = Vec::<OsString>::with_capacity(100);
        if !cfg.project_root.is_empty() {
            let path = Path::new(&cfg.project_root);
            Self::traverse_directory(&path, &mut vec);
        }
        vec
    }
    pub fn generate_compilation_commands(
        &self,
        file_name: &Vec<OsString>,
        cfg: &Config,
    ) -> Vec<OsString> {
        let mut vec = Vec::<OsString>::with_capacity(100);

        for file in file_name.iter() {

            let file_path =Path::new(file);
            let target_file_name =file_path.file_name().unwrap()
                .to_str().unwrap().
                replace(".asm", ".obj");
            
            let mut out_file_name = self.build_dir.clone().unwrap();
            out_file_name.push(target_file_name);

            let out_file_name =out_file_name.as_path();

            let mut command =String::new();

            if cfg.nasm_extra_args_list_file {
                let target_list_file_name =file_path.file_name().unwrap()
                .to_str().unwrap().
                replace(".asm", ".lst");

                let mut list_file_name =self.build_dir.clone().unwrap();
                list_file_name.push(target_list_file_name);

                command = format!(
                "nasm -f {} {} -o {} -l {} {}",
                cfg.nasm_command_target,
                file.to_str().unwrap(),
                out_file_name.to_str().unwrap(),
                list_file_name.to_str().unwrap(),
                cfg.nasm_extra_args
                );
                vec.push(command.into());
            }else{
                let command = format!(
                    "nasm -f {} {} -o {} {}",
                    cfg.nasm_command_target,
                    file.to_str().unwrap(),
                    out_file_name.to_str().unwrap(),
                    cfg.nasm_extra_args
                );
                vec.push(command.into());
            }

        }
        vec
    } //end fn

    fn open_c_img_file(&self, cfg: &Config) -> Option<File> {
        let mut file_path = PathBuf::new();
        file_path.push(cfg.target_hard_file_dir.clone());
        file_path.push(cfg.target_hard_file_name.clone());

        let file_path = file_path.as_path();

        let res = File::options().write(true).open(file_path);
        match res {
            Ok(file) => Some(file),
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
                None
            }
        }
    }

    fn remove_file(&self, path: &Path) {
        let res = std::fs::remove_file(path);
        match res {
            Ok(_) => {}
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
            }
        }
    }

    fn is_exists_c_img(&self, cfg: &Config) -> Option<bool> {
        let mut path = PathBuf::new();
        path.push(cfg.target_hard_file_dir.clone());
        path.push(cfg.target_hard_file_name.clone());
        let path = path.as_path();
        if path.exists() {
            Some(true)
        } else {
            None
        }
    } //end fn

    fn copy_disk_img(&self, cfg: &Config) -> Option<bool> {
        let mut from = PathBuf::new();
        from.push(cfg.original_hard_file.clone());
        let from = from.as_path();

        if !from.exists() {
            println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
            my_exit_process_msg(&format!(
                "原始用于拷贝的虚拟文件{}不存在...",
                cfg.original_hard_file.clone()
            ).red());
            return None;
        }

        let mut to = PathBuf::new();
        to.push(cfg.target_hard_file_dir.clone());
        to.push(cfg.target_hard_file_name.clone());
        let to = to.as_path();
        let res = fs::copy(from, to);
        match res {
            Ok(_) => Some(true),
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
                None
            }
        }
    }

    fn lsn_to_byte_size(&self, lsn_num: u32, sector_size: u32) -> u64 {
        (lsn_num * sector_size)  as u64
    }

    fn read_data(&self, buf: &mut Vec<u8>, file_name: &Path) {
        let res = fs::File::options().read(true).open(file_name);
        match res {
            Ok(mut file) => {
                let res = file.read_to_end(buf);
                match res {
                    Ok(_) => {}
                    Err(e) => {
                        my_exit_process(&e);
                    }
                }
            }
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
            }
        } //end match
    } //end fn

    fn write_data(&self, data: &Vec<u8>, file: &mut File, seek_position: u64) {
        //seek err 没有处理
        //写文件超出文件最大范围没处理
        let res = file.seek(std::io::SeekFrom::Start(seek_position));
        match res {
            Ok(_) => {}
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
            }
        }

        let res = file.write_all(data);
        match res {
            Ok(_) => {}
            Err(e) => {
                println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                my_exit_process(&e);
            }
        }
    }

    fn write_c_img_core(&self, file: &mut File, cfg: &Config) {
        for obj in cfg.bin_file_cfg.iter() {
            let mut path = self.build_dir.clone().unwrap();
            path.push(obj.name.clone());
            let path = path.as_path();

            if !path.is_file() {
                my_exit_process_msg(&format!("{}不是一个文件或不存在", &path.to_str().unwrap()).red());
            }

            let mut buf = Vec::<u8>::new();
            self.read_data(&mut buf, &path);
            let byte_size = self.lsn_to_byte_size(obj.lsn, cfg.hard_sector_size);

            self.write_data(&buf, file, byte_size); //加1,是要跳过byte_size后写入
        }
    }

    pub fn write_img_file(&self, cfg: &Config) {
        let mut path = PathBuf::new();
        path.push(cfg.original_hard_file.clone());
        let path = path.as_path();

        if !path.exists() {
            println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
            my_exit_process_msg(&format!(
                "原始用于拷贝的虚拟文件{}不存在...",
                cfg.original_hard_file.clone()
            ).red());
        } else {
            //判断文件c.img存,则删除
            match self.is_exists_c_img(cfg) {
                Some(_flg) => {
                    let mut path = PathBuf::new();
                    path.push(cfg.target_hard_file_dir.clone());
                    path.push(cfg.target_hard_file_name.clone());
                    let path = path.as_path();
                    self.remove_file(path);
                }
                None => {}
            } //end match remove_file

            //复件文件,并重命名
            match self.copy_disk_img(cfg) {
                Some(_val) => {
                    //跟据cofig文件写入文件到不同的逻辑区号中
                    match self.open_c_img_file(cfg) {
                        Some(mut file) => {
                            self.write_c_img_core(&mut file, cfg);
                        }
                        None => {
                            println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                            my_exit_process_msg(&format!(
                                "打开{}文件失败...",
                                cfg.target_hard_file_name.clone()
                            ).red());
                        }
                    }
                } //end some
                None => {
                    println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                    my_exit_process_msg(&format!(
                        "复制{}文件生成{}失败...",
                        cfg.original_hard_file.clone(),
                        cfg.target_hard_file_name.clone()
                    ).red());
                }
            } //end match
        } //end else 1
    } //end fn

    fn str_to_pcwstr(s: &str) -> Vec<u16> {
    let result = s
        .to_string()
        .encode_utf16()
        .chain(std::iter::once(0))
        .collect();
    result
}

    fn run_bachs_exe(&self, cfg: &Config) {
        unsafe {
            let mut path = PathBuf::new();
            path.push(cfg.target_hard_file_dir.clone());
            path.push("bcdbg.exe");

            let mut app_name: String = path.to_str().unwrap().into();
            app_name.push(' ');
            app_name +=&cfg.bochs_command_args.clone();

            dbg!(&app_name);
            
            let mut app_name = Self::str_to_pcwstr(&app_name);
            app_name.push('\0' as u16);

            let app_name = PWSTR::from_raw(
                    app_name.as_mut_ptr(),
            );

            let mut sta = STARTUPINFOW::default();
            sta.cb = mem::size_of::<STARTUPINFOW>() as _;
            let mut info = PROCESS_INFORMATION::default();
            let _res = CreateProcessW(
                PCWSTR::null(),
                app_name,
                None,
                None,
                true,
                //PROCESS_CREATION_FLAGS::default(),
                //CREATE_NO_WINDOW,
                CREATE_NEW_CONSOLE,
                None,
                PCWSTR::null(),
                &sta,
                &mut info,
            );

            use windows::Win32::Foundation::GetLastError;

            let err: WIN32_ERROR =GetLastError();
            dbg!(&err);

            OpenProcess(PROCESS_ALL_ACCESS,true,info.dwProcessId);
            WaitForSingleObject(info.hProcess, INFINITE);

            if !info.hProcess.is_invalid(){
                CloseHandle(info.hThread).unwrap();
                CloseHandle(info.hProcess).unwrap();
            }

        }
    }

    pub fn run(&mut self) {
        let cfg = Config::init().unwrap();
        //创建build文件夹
        self.build_dir = Self::create_build(&cfg);
        //递归遍历出所有asm文件,生成nasm编译命令
        let nasm_arry = Self::get(&cfg);
        let nasm_commands = self.generate_compilation_commands(&nasm_arry, &cfg);
        //调用命令行程序执行nasm进行编

        //dbg!(&nasm_commands);

        let mut nasm_command_str = String::new();

        for c in nasm_commands.into_iter() {
            nasm_command_str += &(";".to_owned() + &c.into_string().unwrap());
        }

        nasm_command_str.push('\0');

        nasm_command_str.remove(0);

        nasm_command_str = "powershell.exe -Command \"".to_owned() + &nasm_command_str + &"\"";

        //dbg!(&nasm_command_str);

        let mut nasm_command_str = nasm_command_str.encode_utf16().collect::<Vec<u16>>();

        unsafe {
            //MessageBoxW(None, w!("Hello"), w!("World"), MB_ICONINFORMATION | MB_OK);
            let mut application_name: String =
                "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe".into();

            application_name.push('\0');

            let mut sta = STARTUPINFOW::default();
            sta.cb = mem::size_of::<STARTUPINFOW>() as _;
            let mut info = PROCESS_INFORMATION::default();
            let res = CreateProcessW(
                PCWSTR::null(),
                PWSTR::from_raw(nasm_command_str.as_mut_ptr()),
                None,
                None,
                true,
                PROCESS_CREATION_FLAGS::default(),
                //CREATE_NO_WINDOW,
                //CREATE_NEW_CONSOLE,
                None,
                PCWSTR::null(),
                &sta,
                &mut info,
            );
            match res {
                Ok(()) => {}
                Err(e) => {
                    println!("{}",format!("An error occurs on line {} of file {}",std::file!(),std::line!()).red());
                    println!("{}", e);
                }
            }
            WaitForSingleObject(info.hProcess, 0xFFFFFFFF);
            let mut process_exit_code: u32 = 0;
            GetExitCodeProcess(info.hProcess, &mut process_exit_code as *mut u32).unwrap();

            dbg!(&process_exit_code);

             if !info.hProcess.is_invalid(){
                CloseHandle(info.hThread).unwrap();
                CloseHandle(info.hProcess).unwrap();
            }

            //根据配制向虚拟硬盘文件写nasm生成二进制代码文件
            if process_exit_code == 1 {
                my_exit_process_msg(&"调用nasm.exe生成bin文件失败...".red());
            } else {
                //根据配制向虚拟硬盘文件写nasm生成二进制代码文件
                self.write_img_file(&cfg);
                //调用Bachs程序运行
                self.run_bachs_exe(&cfg);
            }
        }
    }
} //end impl

mod tests {

    #[test]
    fn test_core_1() {
        let val = Core::is_exist_build();
        assert!(val == true);
    }

    #[test]
    fn test_core_2() {
        crate::Core::create_build();
        let val = Core::is_exist_build();

        let val = Core::is_exist_build();
        assert!(val == true);
    }
}
